/**************************************************************************************** 
 Copyright © 2003-2012 hbasesoft Corporation. All rights reserved. Reproduction or       <br>
 transmission in whole or in part, in any form or by any means, electronic, mechanical <br>
 or otherwise, is prohibited without the prior written consent of the copyright owner. <br>
 ****************************************************************************************/
package com.hbasesoft.hamibot.controller;

import java.io.File;
import java.util.Date;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.hbasesoft.framework.common.GlobalConstants;
import com.hbasesoft.framework.common.utils.date.DateUtil;
import com.hbasesoft.framework.common.utils.io.HttpUtil;
import com.hbasesoft.framework.common.utils.io.IOUtil;
import com.hbasesoft.framework.common.utils.logger.LoggerUtil;
import com.hbasesoft.framework.common.utils.security.DataUtil;
import com.hbasesoft.framework.message.core.event.EventEmmiter;
import com.hbasesoft.hamibot.Cmd;
import com.hbasesoft.hamibot.entity.AppLogEntity;
import com.hbasesoft.hamibot.service.CmdService;
import com.hbasesoft.hamibot.vo.AppCmdReqVo;
import com.hbasesoft.hamibot.vo.CmdRespVo;
import com.hbasesoft.hamibot.vo.MainCmdReqVo;

import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;

/**
 * <Description> <br>
 * 
 * @author ww200<br>
 * @version 1.0<br>
 * @taskId <br>
 * @CreateDate 2024年12月18日 <br>
 * @since V1.0<br>
 * @see com.hbasesoft.hamibot.controller <br>
 */
@RequestMapping("/cmd")
@RestController
public class CmdController {

    /** */
    private static final String FILE_STORAGE_PATH = GlobalConstants.FILE_STORAGE_PATH + "/hamibot";

    /**
     * cmd service
     */
    @Resource
    private CmdService cmdService;

    /** */
    private Map<String, Queue<CmdRespVo>> cmdQueueMap = new ConcurrentHashMap<>();

    /**
     * Description: 主应用命令 <br>
     * 
     * @author 王伟<br>
     * @taskId <br>
     * @param req
     * @return <br>
     */
    @PostMapping("/main")
    public CmdRespVo mainCmd(final @RequestBody MainCmdReqVo req) {
        log(req);
        CmdRespVo resp = getStackCmd(req.getDeviceId());
        if (resp == null) {
            try {
                if (CollectionUtils.isNotEmpty(req.getAppNames())) {
                    resp = pushQueueCmd(req.getDeviceId(), cmdService.chooseApp(req));
                }
                else {
                    resp = new CmdRespVo();
                    resp.setCode(Cmd.REST.getCode());
                }
            }
            catch (Exception e) {
                LoggerUtil.error(e);
                // 复原
                resp = new CmdRespVo();
                resp.setCode(Cmd.REST.getCode());
            }
        }
        log(req.getDeviceId(), resp);
        return resp;
    }

    /**
     * Description: 子应用命令 <br>
     * 
     * @author 王伟<br>
     * @taskId <br>
     * @param req
     * @return <br>
     */
    @PostMapping("/app")
    public CmdRespVo appCmd(final @RequestBody AppCmdReqVo req) {
        log(req);

        CmdRespVo resp = getStackCmd(req.getDeviceId());
        if (resp == null) {
            try {
                // 如果当前有任务执行，提交任务
                resp = pushQueueCmd(req.getDeviceId(), cmdService.taskSubmit(req));
            }
            catch (Exception e) {
                LoggerUtil.error(e);
                // 复原
                resp = new CmdRespVo();
                resp.setCode(Cmd.REST.getCode());
            }
        }
        log(req.getDeviceId(), resp);
        return resp;
    }

    private CmdRespVo getStackCmd(final String deviceId) {
        if (StringUtils.isEmpty(deviceId)) {
            LoggerUtil.warn("未上报设备Id, 执行结束");
            CmdRespVo resp = new CmdRespVo();
            resp.setCode(Cmd.END.getCode());
            return resp;
        }

        Queue<CmdRespVo> queue = cmdQueueMap.computeIfAbsent(deviceId, (key) -> new ConcurrentLinkedQueue<>());
        if (!queue.isEmpty()) {
            return queue.poll();
        }
        return null;
    }

    private CmdRespVo pushQueueCmd(final String deviceId, final CmdRespVo[] vos) {
        if (ArrayUtils.isNotEmpty(vos)) {
            if (vos.length > 1) {
                for (int i = 1; i < vos.length; i++) {
                    Queue<CmdRespVo> queue = cmdQueueMap.computeIfAbsent(deviceId,
                        (key) -> new ConcurrentLinkedQueue<>());
                    queue.add(vos[i]);
                }
            }
            return vos[0];
        }
        LoggerUtil.warn("未获取到命令，重置后重新执行");
        CmdRespVo resp = new CmdRespVo();
        resp.setCode(Cmd.REST.getCode());
        return resp;
    }

    private String saveFile(final String base64File) {
        if (StringUtils.isNotEmpty(base64File)) {
            Date date = DateUtil.getCurrentDate();
            String dirPath = DateUtil.format(date, DateUtil.DATE_FORMAT_8);
            File dir = new File(FILE_STORAGE_PATH, dirPath);
            if (!dir.exists()) {
                dir.mkdirs();
            }

            String fileName = DateUtil.format(date, "HHmmssSSS") + ".png";
            File file = new File(dir, fileName);
            IOUtil.writeFile(DataUtil.base64Decode(base64File), file);
            return new StringBuilder().append("/api/log/download/").append(dirPath).append('/').append(fileName)
                .toString();
        }
        return null;
    }

    private void log(final AppCmdReqVo req) {
        String screentshot = saveFile(req.getScreenshot());
        req.setScreenshot(null);
        LoggerUtil.info(">>> /app {0}", req);

        AppLogEntity entity = new AppLogEntity();
        entity.setScreenshot(screentshot);
        entity.setDeviceId(StringUtils.isNotEmpty(req.getDeviceId()) ? req.getDeviceId() : GlobalConstants.BLANK);
        RequestAttributes requestAttr = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttr == null ? null : ((ServletRequestAttributes) requestAttr).getRequest();
        entity.setUserAgent(request.getHeader("User-Agent"));
        entity.setContent(req.toString());
        entity.setDirection(0);
        entity.setIp(HttpUtil.getRequestIp(request));

        EventEmmiter.emmit("log", entity);
    }

    private void log(final String deviceId, final CmdRespVo resp) {
        LoggerUtil.info("<<< [{0}]{1}", Cmd.toMap().get(resp.getCode()), resp);

        AppLogEntity entity = new AppLogEntity();
        entity.setDeviceId(deviceId);
        RequestAttributes requestAttr = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttr == null ? null : ((ServletRequestAttributes) requestAttr).getRequest();
        entity.setUserAgent(request.getHeader("User-Agent"));
        entity.setContent(resp.toString());
        entity.setDirection(1);
        entity.setIp(HttpUtil.getRequestIp(request));
        entity.setCmd(resp.getCode());

        EventEmmiter.emmit("log", entity);

    }

    private void log(final @RequestBody MainCmdReqVo req) {
        String screentshot = saveFile(req.getScreenshot());
        req.setScreenshot(null);
        LoggerUtil.info(">>> /main {0}", req);
        AppLogEntity entity = new AppLogEntity();
        entity.setScreenshot(screentshot);
        entity.setDeviceId(StringUtils.isNotEmpty(req.getDeviceId()) ? req.getDeviceId() : GlobalConstants.BLANK);
        RequestAttributes requestAttr = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttr == null ? null : ((ServletRequestAttributes) requestAttr).getRequest();
        entity.setUserAgent(request.getHeader("User-Agent"));
        entity.setContent(req.toString());
        entity.setDirection(0);
        entity.setIp(HttpUtil.getRequestIp(request));

        EventEmmiter.emmit("log", entity);

    }
}
